unit uDZDynamicArray;

interface

uses
  SysUtils, Classes, RTLConsts, uDZAlgorithm;

type
  TDzIntegerDynArray = class
  private
    fArray: array of Integer;
    fSorted: Boolean;
    procedure _Insert(Index, Value: Integer);
    function GetCount: Integer;
    procedure SetCount(const Value: Integer);
    procedure SetSorted(const Value: Boolean);
    function GetItems(Index: Integer): Integer;
    procedure SetItems(Index: Integer; const Value: Integer);
  protected
    procedure CheckIndex(Index: Integer);
  public
    function Exists(Value: Integer): Boolean;
    function IndexOf(Value: Integer): Integer;
    function Insert(Index, Value: Integer): Integer;
    function Add(Value: Integer): Integer;
    procedure Delete(Index: Integer);
    procedure Remove(Value: Integer);
    procedure QuickSort;
    procedure HeapSort;
    property Count: Integer read GetCount write SetCount;
    property Items[Index: Integer]: Integer read GetItems write SetItems; default;
    property Sorted: Boolean read fSorted write SetSorted; 
  end;

function CompareInteger(First, Second: PInteger): Integer;
procedure SwapInteger(First, Second: PInteger);

implementation

function CompareInteger(First, Second: PInteger): Integer;
begin
  Result := First^ - second^;
end;

procedure SwapInteger(First, Second: PInteger);
begin
  First^ := First^ xor Second^;
  Second^ := First^ xor Second^;
  First^ := First^ xor Second^;
end;

{ TDzIntegerDynArray }

function TDzIntegerDynArray.Add(Value: Integer): Integer;
begin
  Result := Insert(High(fArray) + 1, Value);
end;

procedure TDzIntegerDynArray.CheckIndex(Index: Integer);
begin
  if (Index < Low(fArray)) or (Index > High(fArray)) then
    TList.Error(@SListIndexError, Index);
end;

procedure TDzIntegerDynArray.Delete(Index: Integer);
begin
  CheckIndex(Index);
  Move(fArray[Index + 1], fArray[Index], SizeOf(Integer) * (Count - Index - 1));
  SetLength(fArray, Length(fArray) - 1);
end;

function TDzIntegerDynArray.Exists(Value: Integer): Boolean;
begin
  Result := IndexOf(Value) <> -1;
end;

function TDzIntegerDynArray.GetCount: Integer;
begin
  Result := Length(fArray);
end;

function TDzIntegerDynArray.GetItems(Index: Integer): Integer;
begin
  CheckIndex(Index);
  Result := fArray[Index];
end;

procedure TDzIntegerDynArray.HeapSort;
begin
  uDZAlgorithm.HeapSort(@fArray[0], SizeOf(Integer), Length(fArray),
    @CompareInteger, @SwapInteger);
end;

function TDzIntegerDynArray.IndexOf(Value: Integer): Integer;
var
  i: Integer;
begin
  Result := - 1;
  if Length(fArray) = 0 then Exit;
  if Sorted then Result := BinarySearch(@fArray[0], SizeOf(Integer),
    Length(fArray), @CompareInteger, Value)
  else begin
    for i := Low(fArray) to High(fArray) do
    begin
      if fArray[i] = Value then
      begin
        Result := i;
        Exit;
      end;
    end;
  end;
end;

function TDzIntegerDynArray.Insert(Index, Value: Integer): Integer;
begin
  if (Index < Low(fArray)) or (Index > High(fArray) + 1) then
    TList.Error(@SListIndexError, Index);
  if Sorted then Index := BinarySearchInsertPos(fArray, SizeOf(Integer),
    Length(fArray), @CompareInteger, Value);
  _Insert(Index, Value);
  Result := Index;
end;

procedure TDzIntegerDynArray.QuickSort;
begin
  uDZAlgorithm.QuickSort(@fArray[0], SizeOf(Integer), Length(fArray),
    @CompareInteger, @SwapInteger);
end;

procedure TDzIntegerDynArray.Remove(Value: Integer);
var
  Index: Integer;
begin
  Index := IndexOf(Value);
  if (Index >= Low(fArray)) and (Index <= High(fArray)) then Delete(Index);
end;

procedure TDzIntegerDynArray.SetCount(const Value: Integer);
begin
  if (Value < 0) or (Value = Length(fArray)) then Exit;
  if Value < Length(fArray) then SetLength(fArray, Value)
  else if Sorted then raise Exception.Create('')
  else SetLength(fArray, Value);
end;

procedure TDzIntegerDynArray.SetItems(Index: Integer; const Value: Integer);
begin
  CheckIndex(Index);
  if Sorted then
  begin
    if ((Index = Low(fArray)) or (fArray[Index - 1] <= Value)) and
      ((Index = High(fArray)) or (fArray[Index + 1] >= Value)) then
      fArray[Index] := Value;
  end
  else fArray[Index] := Value;
end;

procedure TDzIntegerDynArray.SetSorted(const Value: Boolean);
begin
  if fSorted = Value then Exit;
  fSorted := Value;
  if fSorted  and (Length(fArray) > 0) then QuickSort;
end;

procedure TDzIntegerDynArray._Insert(Index, Value: Integer);
begin
  SetLength(fArray, Length(fArray) + 1);
  Move(fArray[Index], fArray[Index + 1],
    SizeOf(Integer) * (High(fArray) - Index));
  fArray[Index] := Value;
end;

end.
